
package com.party.mem.web.sqlplugins;

import com.party.common.annotation.DataAuthParam;
import com.party.common.constant.Constant;
import com.party.common.spring.SpringUtils;
import com.party.common.utils.LangUtils;
import com.party.common.utils.StringUtils;
import com.party.core.model.commune.CAdminBranch;
import com.party.core.model.member.MemberGroup;
import com.party.core.service.commune.ICAdminBranchService;
import com.party.core.service.commune.impl.CAdminBranchService;
import com.party.core.service.member.IMemberGroupService;
import com.party.core.service.member.impl.MemberGroupService;
import com.party.mem.utils.RealmUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;


/**
 * mybatis数据权限拦截器
 *
 * @author hqh
 * 2018年11月29日
 */
@Intercepts({@Signature(method = "query", type = Executor.class,
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class AuthLcpInterceptor implements Interceptor {

    private static final Logger log = LoggerFactory.getLogger(AuthLcpInterceptor.class);

    private IMemberGroupService memberGroupService;
    private ICAdminBranchService cAdminBranchService;


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取方法上的数据权限注解，如果没有注解，则直接通过
        DataAuthParam dataAuth = getPermissionByDelegate(mappedStatement);
        if (dataAuth == null) {
            return invocation.proceed();
        }

        //初始化bean
        this.loadService();

        String adminId = RealmUtils.getNewCurrentUser().getId();
        MemberGroup memberGroup = memberGroupService.get(adminId);
        // 不是行知公社管理员类型的直接通过
        if (!Constant.MEMBER_XZGSADMIN.equals(memberGroup.getType())) {
            return invocation.proceed();
        }
        //处理权限数据，并返回分社sql
        String orgAuthSql = this.dealOrgAuth(dataAuth, memberGroup);
        //如果sql为空，那直接返回
        if ("".equals(orgAuthSql.trim())) {
            return invocation.proceed();
        }
        //原sql
        String sql = mappedStatement.getBoundSql(invocation.getArgs()[1]).getSql();

        log.info("数据权限拦截器-----原sql：" + sql.replaceAll("\r|\n", ""));
        log.info("数据权限拦截器-----待拼接sql：" + orgAuthSql);
        //处理sql拼接
        String join_sql = this.permissionSql(sql, orgAuthSql, invocation);
        log.info("数据权限拦截器-----拼接后的sql：" + join_sql.replaceAll("\r|\n", ""));
        return invocation.proceed();
    }


    /**
     * 处理组织权限数据，并返回组织权限sql
     *
     * @param dataAuth
     * @param memberGroup
     * @return
     */
    private String dealOrgAuth(DataAuthParam dataAuth, MemberGroup memberGroup) {
        String orgAuthSql = "";
        //表字段注解
        String field = dataAuth.field();
        //模块类型注解
        String moduleType = dataAuth.moduleType();
        //别名注解
        String alias = dataAuth.alias();
        alias = StringUtils.isEmpty(alias) ? "" : alias + ".";

        //获取分配的分社id
        CAdminBranch query = new CAdminBranch();
        query.setAdminId(memberGroup.getId());
        List<CAdminBranch> cAdminBranches = cAdminBranchService.list(query);
        if (cAdminBranches.size() == 0) {
            return alias + field + " in ("+"'" + memberGroup.getId() +"'"+ ")";
        }
        List<String> memberGroupIds = LangUtils.transform(cAdminBranches, input -> input.getBranchId());
        //行知公社id
        memberGroupIds.add(memberGroup.getPartnerId());
        String memberGroupIds_str = StringUtils.stringArray2Strin(memberGroupIds.toArray(new String[memberGroupIds.size()]));
        orgAuthSql = memberGroupIds_str == null ? "" : alias + field + " in (" + memberGroupIds_str + ")";

        return orgAuthSql;
    }

    /**
     * 获取数据权限注解信息
     *
     * @param mappedStatement
     * @return
     */
    private DataAuthParam getPermissionByDelegate(MappedStatement mappedStatement) {
        DataAuthParam dataAuth = null;
        try {
            String id = mappedStatement.getId();
            String className = id.substring(0, id.lastIndexOf("."));
            String methodName = id.substring(id.lastIndexOf(".") + 1, id.length());
            final Class<?> cls = Class.forName(className);
            final Method[] method = cls.getMethods();
            for (Method me : method) {
                if (me.getName().equals(methodName) && me.isAnnotationPresent(DataAuthParam.class)) {
                    dataAuth = me.getAnnotation(DataAuthParam.class);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataAuth;
    }

    /**
     * 权限sql包装
     *
     * @param sql        原sql
     * @param orgAuthSql sql
     * @param invocation
     */
    private String permissionSql(String sql, String orgAuthSql, Invocation invocation) {
        final Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = statement.getBoundSql(parameterObject);
        MappedStatement newStatement = newMappedStatement(statement, new AuthSqlSource(boundSql));
        MetaObject msObject = MetaObject.forObject(newStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
        //sql拼接
        if ((!"".equals(orgAuthSql.trim()))) {
            if (sql.toUpperCase().contains("WHERE")) {
                //最后一个where的位置
                int lastIndex = sql.lastIndexOf("WHERE");
                StringBuffer sb = new StringBuffer(sql);
                sb.insert(lastIndex + 5, " 1=1 AND " + orgAuthSql + " AND");
                sql = sb.toString();
            }
        }
        //sql替换
        msObject.setValue("sqlSource.boundSql.sql", sql);
        args[0] = newStatement;
        return sql;
    }

    /**
     * MappedStatement包装
     *
     * @param ms
     * @param newSqlSource
     * @return
     */
    private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource,
                ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

    /**
     * 加载注入的bean
     */
    private void loadService() {
        if (memberGroupService == null) {
            memberGroupService = (MemberGroupService) SpringUtils.getBean("memberGroupService");
        }
        if (cAdminBranchService == null) {
            cAdminBranchService = (CAdminBranchService) SpringUtils.getBean("cAdminBranchService");
        }

    }
}